﻿using FFmpeg.AutoGen;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace FFmpegWinui3.MediaFolder
{
    public unsafe class VideoMedia : MediaBase
    {
        public override AVMediaType Type { get; protected set; } = AVMediaType.AVMEDIA_TYPE_VIDEO;

        public AVRational BaseFrameRateQ { get; protected set; }
        /// <summary>
        /// 帧率
        /// </summary>
        public double FrameRate { get => ffmpeg.av_q2d(BaseFrameRateQ); }
        public int FrameWidth { get; protected set; }
        public int FrameHeight { get; protected set; }


        private SwsContext* ConvertContext;
        private IntPtr FrameBufferPtr;
        private byte_ptrArray4 DstData;
        private int_array4 DstLinesize;
        public VideoMedia(AVFormatContext* format, int streamIndex) : base(format, streamIndex)
        {
            BaseFrameRateQ = ffmpeg.av_guess_frame_rate(format, Stream, null);
            FrameWidth = Stream->codecpar->width;
            FrameHeight = Stream->codecpar->height;
            base.FrameRate = ffmpeg.av_q2d(Stream->r_frame_rate);
            InitConvert(FrameWidth, FrameHeight, CodecContext->pix_fmt, FrameWidth, FrameHeight, AVPixelFormat.AV_PIX_FMT_BGR0);
        }

        private void InitConvert(int sw, int sh, AVPixelFormat inFormat, int dw, int dh, AVPixelFormat outFormat)
        {
            ConvertContext = ffmpeg.sws_getContext(sw, sh, inFormat, dw, dh, outFormat, ffmpeg.SWS_FAST_BILINEAR, null, null, null);
            if (ConvertContext == null)
                new ArgumentNullException("初始化视频转换失败");

            var bufferSize = ffmpeg.av_image_get_buffer_size(outFormat, dw, dh, 1);
            FrameBufferPtr = Marshal.AllocHGlobal(bufferSize);
            DstData = new byte_ptrArray4();
            DstLinesize = new int_array4();

            ffmpeg.av_image_fill_arrays(ref DstData, ref DstLinesize, (byte*)FrameBufferPtr, outFormat, dw, dh, 1);
        }

        private AVFrame Convert(AVFrame* sourceFrame)
        {
            ffmpeg.sws_scale(ConvertContext, sourceFrame->data, sourceFrame->linesize, 0, sourceFrame->height, DstData, DstLinesize);
            var data = new byte_ptrArray8();
            data.UpdateFrom(DstData);
            var linesize = new int_array8();
            linesize.UpdateFrom(DstLinesize);

            return new AVFrame
            {
                data = data,
                linesize = linesize,
                width = FrameWidth,
                height = FrameHeight
            };
        }

        public override byte[] GetNextFrameData(TimeSpan time)
        {
            var tempFrame = GetNextFrame(time);
            if (tempFrame == CurrentFrame)
                return null;

            DisposeFrame();
            CurrentFrame = tempFrame;
            if (CurrentFrame == null)
                return null;

            var result = Convert(CurrentFrame.frame);
            byte[] bytes = new byte[FrameWidth * FrameHeight * 4];
            Marshal.Copy((IntPtr)result.data[0], bytes, 0, bytes.Length);
            return bytes;
        }
    }
}
